/*
 * MySQL Cluster SNMP monitoring subagent
 * Copyright (C) 2011, Steven Ayre <steveayre@gmail.com>
 *
 * Version: MPL 1.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is MySQL Cluster SNMP monitoring subagent
 *
 * The Initial Developer of the Original Code is
 * Steven Ayre <steveayre@gmail.com>
 * Portions created by the Initial Developer are Copyright (C)
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *
 * Steven Ayre <steveayre@gmail.com>
 *
 * ndb_mgm.c -- NDB MGM thread code
 *
 */

#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/time.h>
#include <arpa/inet.h>
#include <libdaemon/daemon.h>
#include <ndb_version.h>
#include <mgmapi/mgmapi.h>
#include "ndb_snmpd.h"
#include "common.h"
#include "ndb_mgm.h"

/* TODO refactor so it can handle losing the connection */
int ndb_mgm_main(thread_t *thread, void *param)
{
	cluster_t *cluster = (cluster_t*)param;
	int status = 0, i;
	NdbMgmHandle mgm = NULL;

	/* Initialise node list */
	memset(&cluster->ndb_nodes, 0, sizeof(cluster->ndb_nodes));
	pthread_mutex_init(&cluster->ndb_nodes.mutex, NULL);

	/* Connect to NDB MGM */
	if (!(mgm = ndb_mgm_create_handle())) {
		daemon_log(LOG_ERR, "ndb_mgm_create_handle: failed");
		status = -1;
		goto end;
	}
	daemon_log(LOG_INFO, "%s: Connecting to %s @ %s", thread_name(thread), cluster->info.name, cluster->info.connectstring);
	if (ndb_mgm_set_connectstring(mgm, cluster->info.connectstring) < 0) {
		daemon_log(LOG_ERR, "ndb_mgm_set_connectstring: failed");
		status = -1;
		goto end;
	}
	if (ndb_mgm_connect(mgm, 0, 0, 0) < 0) {
		daemon_log(LOG_ERR, "ndb_mgm_set_connect: failed");
		status = -1;
		goto end;
	}

	/* Poll the node statuses */
	/* TODO get initial status, then update on change */
	while(thread_running(thread)) {
		struct ndb_mgm_cluster_state *state = NULL;
		int mgmt_idx = 0, data_idx = 0, api_idx = 0;

		if (!(state = ndb_mgm_get_status(mgm))) {
			daemon_log(LOG_ERR, "ndb_mgm_get_status: failed");
			status = -1;
			goto end;
		}

		pthread_mutex_lock(&cluster->ndb_nodes.mutex);

		for(i=0; i < state->no_of_nodes; i++)
		{
			struct ndb_mgm_node_state *node_state = &state->node_states[i];
			ndb_node_table_t *table;
			ndb_node_entry_t *entry;
			int idx;

			/* Determine the node type */
			switch(node_state->node_type) {
			case NDB_MGM_NODE_TYPE_MGM:
				idx = mgmt_idx++;
				table = &cluster->ndb_nodes.mgmt;
				break;
			case NDB_MGM_NODE_TYPE_NDB:
				idx = data_idx++;
				table = &cluster->ndb_nodes.data;

				break;
			case NDB_MGM_NODE_TYPE_API:
				idx = api_idx++;
				table = &cluster->ndb_nodes.api;
				break;
			default:
				continue;
			}

			/* Add entry to table, expand if required */
			if (table->size <= idx) {
				ndb_node_entry_t *new_nodes;
				int new_size = idx + 1;
				if (!(new_nodes = (ndb_node_entry_t*)realloc(table->nodes, sizeof(ndb_node_entry_t)*new_size))) {
					daemon_perror(LOG_ERR, "realloc");
					status = -1;
					pthread_mutex_unlock(&cluster->ndb_nodes.mutex);
					goto end;
				}
				table->size  = new_size;
				table->nodes = new_nodes;
			}
			entry = &table->nodes[idx];

			/* Update common properties regardless of connection state */
			entry->cluster_idx = cluster->info.idx;
			entry->idx = idx+1;
			entry->id = node_state->node_id;

			/* Is node connected? */
			if (node_state->version != 0) {
				/* Update common properties */
				entry->connected = 1;
				inet_pton(AF_INET, node_state->connect_address, &entry->ip_addr.v4);
				entry->addr_family = AF_INET;
				ndbGetVersionString(node_state->version, node_state->mysql_version, 0, entry->version, sizeof(entry->version));

				/* Update type-specific properties */
				switch(node_state->node_type) {
				case NDB_MGM_NODE_TYPE_NDB:
					entry->nodegroup = node_state->node_group;
					break;
				default:
					break;
				}
			}
			else {
				/* Update common properties*/
				entry->connected = 0;
				memset(entry->version, 0, sizeof(entry->version));
			}
		}

		cluster->ndb_nodes.mgmt.num_nodes = mgmt_idx;
		cluster->ndb_nodes.data.num_nodes = data_idx;
		cluster->ndb_nodes.api.num_nodes  = api_idx;

		pthread_mutex_unlock(&cluster->ndb_nodes.mutex);

		free(state);

		thread_yield(thread, 5000000);
	}

  end:
	/* Cleanup */
	if (mgm) {
		ndb_mgm_disconnect(mgm);
		ndb_mgm_destroy_handle(&mgm);
	}

	/* Return the status */
	return status;
}

